package core.simple;

import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Arrays;

import core.Problem;
import java.util.Random;

public class Population implements Iterable<Indyvidual> {
	List<Indyvidual> population;
	int maxNoOfIndyviduals = -1;
	boolean isLimited = false;
	Random generator = new Random();
	
	//constructor creating empty Population - default
	public Population(){
		this.population = new ArrayList<Indyvidual>();
	}
	public Population(int max){
		this.population = new ArrayList<Indyvidual>();
		if (max>0){
			this.maxNoOfIndyviduals = max;
			isLimited = true;
		}
		
	}
	public void reset(){
		population.clear();
		
	}
	public Iterator<Indyvidual> iterator(){
		return population.iterator();
			
	}
	public int getSize(){
		return population.size();
	}

	public Indyvidual addGenotype(double [] genotype){
		Indyvidual tmpIndyvidual = new Indyvidual(genotype);
		population.add(tmpIndyvidual);
		return tmpIndyvidual;
	}
	public void add(double [] genotype,double fitness, double [] results){
		Indyvidual tmpIndyvidual = new Indyvidual(genotype,fitness,results);
		population.add(tmpIndyvidual);	
	}
	public Indyvidual tournamentSelection(){
		Indyvidual i1,i2;
		int size = population.size();
		i1 = population.get(generator.nextInt(size));
		i2 = population.get(generator.nextInt(size));
		if (i1.fitness<i2.fitness)
			return i1;
		else
			return i2;
	}
	//only when need to truncate archive
	public void countDistances(Problem problem){
		
		/*Iterator<Indyvidual> it1 = population.iterator();
		Iterator<Indyvidual> it2;*/
		double [] g1,g2,val1,val2;
		Indyvidual i1,i2;
//		Map<int[],Double> map = new HashMap<int[],Double>();
		int i,j1=0,j2=0;
		double sum;
		
		while (j1 < population.size()){
			i1 = population.get(j1);
			g1 = i1.genotype;
			int [] key = new int[2];
			key[0]=g1.hashCode();
//			it2 = population.iterator();
			double [] distances = new double [population.size()];
			int index = 0;
			j2 = 0;
			while (j2 < population.size()){

				i2 = population.get(j2);
				g2 = i2.genotype;
				
/*				key[1] = g2.hashCode();
				
				if (!map.containsKey(key)){*/
					i = 0;
					sum = 0.0;
					val1 = problem.calculate(g1);
					val2 = problem.calculate(g2);
					try {
						while(true) {
							sum += Math.pow(val1[i]-val2[i],2);
							i++;
						}
					} catch (Exception e){
						//who cares
					}
					sum = Math.sqrt(sum);
/*					map.put(key, sum);
					i = key[0];
					key[0] = key[1];
					key[1] = i;
					map.put(key, sum);
					key[0] = i;*/
					distances[index] = sum;
					
/*				} 
				else {
					
					distances[index] = map.get(key);
				}*/
				index++;
				j2++;
			}
			Arrays.sort(distances);
			i1.distances = distances;
			j1++;

		}
	}
	/**
	 * @return
	 */
	public List<Indyvidual> truncate(){
		List<Indyvidual> tmp = this.population;
		Iterator<Indyvidual> it1 = tmp.iterator()/*,it2*/;
		Indyvidual i1,i2;
		int i,max/*,e,l,e1,l1,j1=0,j2=0*/;
//		boolean removed = false;
//		while (!removed)
//			while (j1 < tmp.size()){
//				e = 0;
//				l = 0;
//				i1 = tmp.get(j1);
//				max = i1.distances.length;
//				j2 = 0;
//				
//				while (j2 < tmp.size()){
//					e1 = 0;
//                    l1 = 0;
//                    i = 0;
//                    i2 = tmp.get(j2);
//    				while ((i < max) && (i1.distances[i] == i2.distances[i])){
//    					i++;
//    					e1 = 1;
//    				}
//    				if ((i < max)&&(i1.distances[i] < i2.distances[i])){
//    					l1 = 1;
//    				}
//    				if (e1+l1==0)
//                        break;
//                    e += e1;
//                    l += l1;
//                    j2++;
//				}
//				if (e+l >= tmp.size()){
//	
//                    tmp.remove(i1);
//                    removed = true;
//                    break;
//				}
//				j1++;
//
//				
//			}
		//System.out.print("Remove!");
		while (this.maxNoOfIndyviduals < tmp.size()){
			it1 = tmp.iterator();
			i1 = it1.next();
			max = i1.distances.length;
			i2 = i1;
			while (it1.hasNext()){
				i1 = it1.next();
				i = 0;
				while ((i < max) && (i1.distances[i]==i2.distances[i])){
					i++;
				}
				if ((i < max)&&(i1.distances[i]<i2.distances[i])){
					i2 = i1;
				}
			}
			tmp.remove(i2);
		}
		return tmp;
	}
	public void countFitness(Population p_t, Problem problem){
		
		List<Indyvidual> tmpList = new ArrayList<Indyvidual>();
		tmpList.addAll(p_t.population);
		tmpList.addAll(this.population);

		
		if (this.getSize()+p_t.getSize()!=tmpList.size()) {
//			System.out.println("Zonk!");
			
		}
		Iterator<Indyvidual> it1 = tmpList.iterator();
		Iterator<Indyvidual> it2;
		Map<int[],Double> map = new HashMap<int[],Double>();
		int i;
		double sum;
		double [] g1,g2,val2,val1;
		int k = (int)Math.round(Math.sqrt(tmpList.size()));
		Indyvidual i1,i2;
		//count strength
		while (it1.hasNext()){
			
			i1 = it1.next();
			i1.strength = 0;
			i1.rawFitness = 0;
			g1 = i1.genotype;
			int [] key = new int[2];
			key[0]=g1.hashCode();
			double [] distances = new double [tmpList.size()];
			int index = 0;
			it2 = tmpList.iterator();
			while (it2.hasNext()){
				i2 = it2.next();
				g2 = i2.genotype;
				
				//count strength
				try {
					if (problem.isFirstDominatingTheOther(i1.results, i2.results)){
						
						i1.strength++;
					}

				} catch (Exception e){
					//who cares?
				}
				//count distances
				key[1]=g2.hashCode();
				
				if (!map.containsKey(key)){
					i = 0;
					sum = 0.0;
					val1  =i1.results;
					val2 = i2.results;
					try {
						while(true) {
							sum += Math.pow(val1[i]-val2[i],2);
							i++;
						}
					} catch (Exception e){
						//who cares
					}
					sum = Math.sqrt(sum);
					map.put(key, sum);
					i = key[0];
					key[0] = key[1];
					key[1] = i;
					map.put(key, sum);
					key[0] = i;
					distances[index] = sum;
					
				}
				else {
					
					distances[index] = map.get(key);
				}
				index++;
			}
			//count density
			Arrays.sort(distances);
			i1.density = 1.0 / (distances[k]+2.0);
			
		}
		//count raw fitness
		
		it1 = tmpList.iterator();
		while (it1.hasNext()){
			i1 = it1.next();
			
			it2 = tmpList.iterator();
			while (it2.hasNext()){
				i2 = it2.next();

				try {
					if (problem.isFirstDominatingTheOther(i1.results, i2.results)){
						i2.rawFitness+=i1.strength;
					}

				} catch (Exception e){
					//who cares?
				}
			}
		}
		//count fitness
		i = 0;
		it1 = tmpList.iterator();
		while (it1.hasNext()){
			i1 = it1.next();
			i1.fitness = i1.density + (double)i1.rawFitness;
			if (i1.fitness>1)
				i++;

		}
	//	System.out.println("A tyle jest kiepskich: " + i);
		
	}
	public Set<double[]> getGenotypes(){
		Iterator<Indyvidual> it = this.population.iterator();
		Set<double[]> set = new HashSet<double[]>();
		while (it.hasNext()){
			set.add(it.next().genotype);
		}
		return set;
	}

}


